﻿/*
 * 本代码受中华人民共和国著作权法保护，作者仅授权下载代码之人在学习和交流范围内
 * 自由使用与修改代码；欲将代码用于商业用途的，请与作者联系。
 * 使用本代码请保留此处信息。作者联系方式：ping3108@163.com, 欢迎进行技术交流
 */

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace ICSharpCode.WinFormsUI.Core
{
    public delegate GraphicsPath ButtonForePathGetter(Rectangle rect);

    public enum ButtonState
    {
        Normal,
        Hover,
        Pressed,
        PressLeave,
    }

    public enum ButtonBorderType
    {
        Rectangle,
        Ellipse,
    }

    public enum MouseOperationType
    {
        Move,
        Down,
        Up,
        Hover,
        Leave,
    }

    public class RenderControlButton
    {
        private Form _owner;
        private ButtonState _state;
        private EventHandler _click;
        private bool _capture;

        #region properties

        /// <summary>
        /// 是否在这个按钮上按下了鼠标未释放
        /// </summary>
        public bool Capture
        { get { return _capture; } }

        public Size BtnSize
        {
            get { return Bounds.Size; }
        }

        public Point Location
        {
            get { return Bounds.Location; }
        }

        public ButtonForePathGetter ForePathGetter { get; set; }

        public event EventHandler Click
        {
            add { _click = value; }
            remove { _click = null; }
        }

        public ButtonState State
        {
            get { return _state; }
            set
            {
                if (value != _state)
                {
                    _state = value;
                    _owner.Invalidate(Bounds);
                }
            }
        }

        public bool Visible { get; set; }
        public Rectangle Bounds { get; set; }        
        public Font ForeFont { get; set; }
        public string Text { get; set; }
        public Size ForePathSize { get; set; }

        public ButtonColorTable ColorTable { get; set; }
        public ButtonBorderType BorderType { get; set; }
        public bool DrawLightGlass { get; set; }
        /// <summary>
        /// 画两次可以加深颜色
        /// </summary>
        public bool DrawForePathTwice { get; set; }

        /// <summary>
        /// 用于在click事件中传回数据
        /// </summary>
        public object ClickSendBackOject { get; set; }        

        public Image BackImageNormal { get; set; }
        public Image BackImageHover { get; set; }
        public Image BackImagePressed { get; set; }

        #endregion

        #region private method

        private void RenderNormal(System.Drawing.Graphics g)
        {            
            RenderInternal(g, BackImageNormal, ColorTable.BorderColorNormal, 
                ColorTable.BackColorNormal, ColorTable.ForeColorNormal);            
        }

        private void RenderHover(System.Drawing.Graphics g)
        {
            RenderInternal(g, BackImageHover, ColorTable.BorderColorHover, 
                ColorTable.BackColorHover, ColorTable.ForeColorHover);
        }

        private void RenderPressed(System.Drawing.Graphics g)
        {            
            RenderInternal(g, BackImagePressed, ColorTable.BorderColorPressed, 
                ColorTable.BackColorPressed, ColorTable.ForeColorPressed);            
        }

        private void RenderInternal(System.Drawing.Graphics g, Image backImage, Color borderColor,
            Color backColor, Color foreColor)
        {
            Region oldClip = g.Clip;
            Region newClip = new Region(Bounds);
            g.Clip = newClip;

            if (backImage != null)
            {
                g.DrawImage(backImage, Bounds);
            }
            else
            {
                FillInBackground(g, backColor);
                DrawBorder(g, borderColor);
                RenderForePathAndText(g, foreColor);
            }

            g.Clip = oldClip;
            newClip.Dispose();
        }

        private void FillInBackground(System.Drawing.Graphics g, Color backColor)
        {
            using (SolidBrush sb = new SolidBrush(backColor))
            {
                if (BorderType != ButtonBorderType.Ellipse)
                {
                    g.FillRectangle(sb, Bounds);
                    return;
                }                
                g.FillEllipse(sb, Bounds);
                if (DrawLightGlass)
                {
                    using (GraphicsPath gp = new GraphicsPath())
                    {
                        Rectangle rect = Bounds;
                        gp.AddEllipse(rect);
                        using (PathGradientBrush pb = new PathGradientBrush(gp))
                        {
                            pb.CenterPoint = new PointF(rect.X + rect.Width / 2.0f, rect.Y + rect.Height / 4.0f);
                            pb.CenterColor = Color.FromArgb(180, Color.White);
                            pb.SurroundColors = new Color[] { Color.FromArgb(40, Color.White) };
                            g.FillPath(pb, gp);
                        }
                    }
                }                                
            }
        }

        private void DrawBorder(System.Drawing.Graphics g, Color borderColor)
        {
            Rectangle rect2 = Bounds;
            rect2.Width--;
            rect2.Height--;
            using (Pen p = new Pen(borderColor))
            {
                if (BorderType == ButtonBorderType.Ellipse)
                    g.DrawEllipse(p, rect2);
                else
                    g.DrawRectangle(p, rect2);
            }
        }

        private void RenderForePathAndText(System.Drawing.Graphics g, Color foreColor)
        {
            if (ForePathGetter != null)
            {
                if(string.IsNullOrEmpty(Text))
                    PathOnly(g,foreColor);
                else
                    PathAndText(g,foreColor);
            }
            else if(!string.IsNullOrEmpty(Text))
                TextOnly(g,foreColor);
        }

        private void PathOnly(System.Drawing.Graphics g, Color foreColor)
        {
            using (GraphicsPath path = ForePathGetter(Bounds))
            {
                using (Pen p2 = new Pen(foreColor))
                {
                    g.DrawPath(p2, path);
                    if (DrawForePathTwice)
                        g.DrawPath(p2, path);
                }
            }
        }

        private void TextOnly(System.Drawing.Graphics g, Color foreColor)
        {
            Size size = TextRenderer.MeasureText(Text, ForeFont);
            int x = Bounds.Left + (Bounds.Width - size.Width) / 2;
            int y = Bounds.Top + (Bounds.Height - size.Height) / 2;
            Rectangle textRect = new Rectangle(new Point(x, y), size);
            TextRenderer.DrawText(
                g,
                Text,
                ForeFont,
                textRect,
                foreColor,
                TextFormatFlags.HorizontalCenter |
                TextFormatFlags.VerticalCenter);
        }

        private void PathAndText(System.Drawing.Graphics g, Color foreColor)
        {
            Size size = TextRenderer.MeasureText(Text, ForeFont);            
            Rectangle rect = Bounds;
            rect.Offset(-size.Width / 2 + 2, 0);
            using (GraphicsPath path = ForePathGetter(rect))
            {
                using (Pen p = new Pen(foreColor))
                {
                    g.DrawPath(p, path);
                }
                int x = rect.Left + rect.Width / 2 + ForePathSize.Width / 2 + 1;
                int y = rect.Top + (rect.Height - size.Height) / 2;
                Rectangle textRect = new Rectangle(new Point(x, y), size);
                TextRenderer.DrawText(
                    g,
                    Text,
                    ForeFont,
                    textRect,
                    foreColor,
                    TextFormatFlags.HorizontalCenter |
                    TextFormatFlags.VerticalCenter);
            }
        }

        #region mouse operation

        private void MouseDown(Point location)
        {
            if (Bounds.Contains(location))
            {
                State = ButtonState.Pressed;
                _capture = true;
            }
            else
            {
                _capture = false;
            }
        }

        private void MouseMove(Point location)
        {
            if (Bounds.Contains(location))
            {                
                if (State == ButtonState.Normal)
                {
                    // 没有在窗体其他地方按下按钮
                    if (!_owner.Capture) 
                    {
                        State = ButtonState.Hover;
                    }
                }
                else if (State == ButtonState.PressLeave)
                {
                    State = ButtonState.Pressed;
                }
                
            }
            else
            {
                if (_capture)
                {
                    State = ButtonState.PressLeave;
                }
                else
                {
                    State = ButtonState.Normal;
                }
            }
        }

        private void MouseLeave(Point location)
        {
            State = ButtonState.Normal;
            _capture = false;
        }

        private void MouseUp(Point location)
        {
            
            if (Bounds.Contains(location))
            {
                State = ButtonState.Hover;
                if (_capture)
                    OnClick(EventArgs.Empty);                
            }
            else
            {
                State = ButtonState.Normal;
            }
            _capture = false;
        }

        #endregion

        #endregion

        #region event

        protected virtual void OnClick(EventArgs e)
        {
            if (_click != null)
            {
                object obj = (ClickSendBackOject != null) ? ClickSendBackOject : this;
                _click(obj, e);
            }
        }

        #endregion

        public RenderControlButton(Form owner)
        {
            _owner = owner;
            _state = ButtonState.Normal;
            Visible = true;
            BorderType = ButtonBorderType.Rectangle;
            DrawLightGlass = false;
        }

        public void DrawButton(System.Drawing.Graphics g)
        {
            if (!Visible)
                return;

            switch (State)
            {
                case ButtonState.Hover:
                    RenderHover(g);
                    break;
                case ButtonState.Pressed:
                    RenderPressed(g);
                    break;
                default:
                    RenderNormal(g);
                    break;
            }
        }

        public void MouseOperation(Point location, MouseOperationType type)
        {
            if (!Visible)
                return;

            switch (type)
            {
                case MouseOperationType.Move:
                    MouseMove(location);
                    break;

                case MouseOperationType.Down:
                    MouseDown(location);
                    break;

                case MouseOperationType.Up:
                    MouseUp(location);
                    break;

                case MouseOperationType.Leave:
                    MouseLeave(location);
                    break;

                default:
                    break;
            }
        }
    }
}
